﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Helpers;
using MQTTnet.Formatter;
using System.Security.Authentication;
using MQTTnet.Extensions.WebSocket4Net;
using JESAI.MQTT.Client.Configurations;
using JESAI.MQTT.Client.Enums;
using MQTTnet.Server;
using JESAI.Core.Util.Threading.Asyncs;

namespace JESAI.MQTT.Client.Clients
{
    public class MQTTClient:IDisposable
    {
        public MqttFactory mqttFactory;
        public IMqttClient mqttClient;
        public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync;
        public event Func<MqttClientCertificateValidationEventArgs, bool> CertificateValidationHandler;
        public static MQTTClientConfig MQTTClientConfig;
        public static MQTTClient mQTTClient;

        public static MQTTClient Instance
        {
            get
            {
                return AsyncHelper.RunSync(() => { return CreateMQTTClient(); });
            }
        }
        private static object mLock = new object();

        private static async Task<MQTTClient> CreateMQTTClient()
        {
            if (MQTTClientConfig == null)
            {
                throw new Exception("请先设置MQTTClientConfig!");
            }

            if (mQTTClient == null)
            {
                lock (mLock)
                {
                    if (mQTTClient == null)
                    {
                        mQTTClient = new MQTTClient(MQTTClientConfig);
                    }
                }

             
            }
            if (!mQTTClient.mqttClient.IsConnected)
            {
                await mQTTClient.Connect_Client();
                await mQTTClient.ReConnection(ReConnectionType.UseEvent);
            }
            return mQTTClient;
        }

        public MQTTClient(MQTTClientConfig mQTTClientConfig)
        {
            mqttFactory=new MqttFactory();
            if (mQTTClientConfig.IsUseWebSocket4Net)
            {
                mqttFactory = mqttFactory.UseWebSocket4Net();
            }

            MQTTClientConfig = mQTTClientConfig;
            mqttClient = mqttFactory.CreateMqttClient();
            mQTTClient = this;
        }

        public  MQTTClient AddSubsctibeHandle()
        {
           
            if (ApplicationMessageReceivedAsync!= null){
                mqttClient.ApplicationMessageReceivedAsync += ApplicationMessageReceivedAsync;
            } 
             //mQTTClient.Connect_Client().Wait();
             //mQTTClient.ReConnection(ReConnectionType.UseEvent).Wait();
            return this;
        }

        /*
        *此示例以干净的方式断开连接。这将发送一个MQTT DISCONNECT数据包
        *然后关闭连接。
        *有关更多详细信息，请参见示例_Connect_Client_。
        */
        //这将发送DISCONNECT数据包。在没有DisconnectAsync的情况下调用_Dispose_
        //连接以“不干净”的方式关闭。有关更多详细信息，请参阅MQTT规范。
        private async Task<MQTTClient> Clean_Disconnect(IMqttClient mqttClient)
        {         
            await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
            return this;
        }

        public async Task<MQTTClient> Connect_Client()
        {
            /*
           *此示例创建了一个简单的MQTT客户端，并连接到一个公共代理。
            *
            *当客户端不再使用时，请始终对其进行处置。
            *MQTT的默认版本是3.1.1。
            */

            // Use builder classes where possible in this project.
            var mqttClientOptionsBuilder = new MqttClientOptionsBuilder();
            if (MQTTClientConfig.IsUseWebSocket)
            {
                /*
                *此示例创建一个简单的MQTT客户端，并使用WebSocket连接连接到公共代理。
                *使用的不是WebSockets的.NET实现，而是来自WebSocket4Net的实现。它提供了更多
                *加密算法，并支持更多平台。
                *
                *这是示例_Connect_Client_的修改版本！有关更多详细信息，请参阅其他示例。
                */
                //"broker.hivemq.com:8000/mqtt"
                mqttClientOptionsBuilder = mqttClientOptionsBuilder.WithWebSocketServer(MQTTClientConfig.Server);
            }
            else
            {
                if (MQTTClientConfig.Port != 0)
                {
                    mqttClientOptionsBuilder = mqttClientOptionsBuilder.WithTcpServer(MQTTClientConfig.Server, MQTTClientConfig.Port);
                }
                else
                {
                    mqttClientOptionsBuilder = mqttClientOptionsBuilder.WithTcpServer(MQTTClientConfig.Server);
                }
               
            }
                
            if (MQTTClientConfig.IsWithoutPacketFragmentation)
            {
                // Disabling packet fragmentation is very important!  
                mqttClientOptionsBuilder = mqttClientOptionsBuilder.WithoutPacketFragmentation();
            }

            /*
             *此示例创建了一个简单的MQTT客户端，并使用MQTT v5连接到公共代理。
             *
             *这是示例_Connect_Client_的修改版本！有关更多详细信息，请参阅其他示例。
              */
            // In MQTTv5 the response contains much more information.
            if (MQTTClientConfig.MqttProtocolVersion!= MqttProtocolVersion.Unknown)
            {
                mqttClientOptionsBuilder = mqttClientOptionsBuilder.WithProtocolVersion(MQTTClientConfig.MqttProtocolVersion);
            }
          
         
            /*
           *此示例创建了一个简单的MQTT客户端，并使用TLS 1.2加密连接到公共代理。
           *
           *这是示例_Connect_Client_的修改版本！有关更多详细信息，请参阅其他示例。
           */
            if (MQTTClientConfig.SslProtocols != SslProtocols.None)
            {
                mqttClientOptionsBuilder.WithTls(
                        o =>
                        {
                            //所使用的公共代理有时具有无效证书。此示例接受所有
                            //证书。这不应该在生活环境中使用。
                            o.CertificateValidationHandler = _ => true;
                            if (CertificateValidationHandler != null)
                            {
                                o.CertificateValidationHandler= CertificateValidationHandler;
                            }
                            //o.CertificateValidationHandler = eventArgs =>
                            //{
                            //    //eventArgs.Certificate.Subject.DumpToConsole();
                            //    //eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
                            //    //eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
                            //    //eventArgs.Chain.ChainStatus.DumpToConsole();
                            //    //eventArgs.SslPolicyErrors.DumpToConsole();
                            //    return true;
                            //};
                            // The default value is determined by the OS. Set manually to force version.
                            o.SslProtocol = MQTTClientConfig.SslProtocols;
                        });
            }

            mqttClientOptionsBuilder.WithCredentials(MQTTClientConfig.UserName, MQTTClientConfig.Password);

            var mqttClientOptions = mqttClientOptionsBuilder.Build();
            mqttClientOptions.ClientId=Guid.NewGuid().ToString();
            
            MqttClientConnectResult response;

            //是否设置超时时间
            if (MQTTClientConfig.TimeOut != 0)
            {
                using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(MQTTClientConfig.TimeOut)))
                {
                    response= await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
                }
            }
            else
            {
                 response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
            }

            //如果服务器不可用，这将引发异常。
            //此消息的结果返回已发送的其他数据
            //从服务器。有关详细信息，请参阅MQTT协议规范。
           
            Console.WriteLine("The MQTT client is connected.");
            response.DumpToConsole();
            return this;
        }

        /// <summary>
        /// PING服务器
        /// </summary>
        /// <returns></returns>
        public async Task<MQTTClient> Ping_Server()
        {
            /*
            *此示例向服务器发送一个PINGREQ数据包，并等待答复。
            *
            *这仅在MQTT v5.0.0+中受支持。
            */
            // This will throw an exception if the server does not reply.
            await mqttClient.PingAsync(CancellationToken.None);

            return this;
        }

        /// <summary>
        /// 使用事件重新连接
        /// </summary>
        /// <returns></returns>
        public async Task<MQTTClient> Reconnect_Using_Event()
        {
            /*
            *此示例显示了在断开连接时如何重新连接。
            *这种方法使用来自客户端的一个事件。
            *这种方法有死锁的风险！考虑使用定时器方法（请参阅示例）。
             */         
            mqttClient.DisconnectedAsync += async e =>
            {
                if (e.ClientWasConnected)
                {
                    // Use the current options as the new options.
                    await mqttClient.ConnectAsync(mqttClient.Options);
                }
            };

            return this;
        }

        /// <summary>
        /// 使用计时器重新连接
        /// </summary>
        public  void Reconnect_Using_Timer()
        {
            /*
            *此示例显示了在断开连接时如何重新连接。
            *这种方法使用一个自定义的任务/线程来监控连接状态。
            *这是推荐的方式，但需要更多的自定义代码！
             */
            _ = Task.Run(
            async () =>
            {
                // User proper cancellation and no while(true).
                while (true)
                {
                    try
                    {                             
                        // This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
                        if (!await mqttClient.TryPingAsync())
                        {
                            await mqttClient.ConnectAsync(mqttClient.Options, CancellationToken.None);

                            // Subscribe to topics when session is clean etc.
                            Console.WriteLine("The MQTT client is connected.");
                        }
                    }
                    catch
                    {
                        // Handle the exception properly (logging etc.).
                    }
                    finally
                    {
                        // Check the connection state every 5 seconds and perform a reconnect if required.
                        await Task.Delay(TimeSpan.FromSeconds(5));
                    }
                }
            });            
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <returns></returns>
        public async Task<MQTTClient> DisConnection()
        {
            await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
            return this;
        }
        /// <summary>
        /// 断开连接
        /// </summary>
        /// <returns></returns>
        public async  Task<MQTTClient> DisConnection(CleanType cleanType)
        {
            switch (cleanType)
            {
                case CleanType.No_Clean:
                    Dispose();
                    break;
                case CleanType.Clean:
                    await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
                    break;
                default:
                    await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
                    break;
            }

            return this;
        }

        /// <summary>
        /// 重新连接
        /// </summary>
        /// <returns></returns>
        public async Task<MQTTClient> ReConnection()
        {
            await Reconnect_Using_Event();
            return this;
        }

        /// <summary>
        /// 重新连接
        /// </summary>
        /// <returns></returns>
        public async Task<MQTTClient> ReConnection(ReConnectionType reConnectionType)
        {
            switch (reConnectionType)
            {
                case ReConnectionType.UseEvent:
                    await Reconnect_Using_Event();
                    break;
                case ReConnectionType.UseTimer:
                    Reconnect_Using_Timer();
                    break;
            }
            return this;
        }

        /// <summary>
        /// 销毁
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public void Dispose()
        {
            mqttClient.Dispose();
        }
 
    }
}
